#include "shell.h"
#include "../fs/FileSystem.h"

using namespace std;

#define FS_READY 1
#define FS_NOT_READY 0
#define PROMPT "cs560_sh$ "

static bool fs_state = FS_NOT_READY;

int start_shell(FileSystem * cur_fs) {
    vector<string> tokeninput;
    string userinput;
    string tempstring;
    int r;
    int temp;

    while (1) {

        cout << PROMPT;

        getline(cin, userinput);
        if(cin.eof()){
            //in case the program is fed a script (using <) and there is not an exit line at the end
            userinput = "exit";
        }

        tokeninput = tokenizeString(userinput);

        if (!tokeninput.empty()) {
            if (tokeninput[0].compare("exit") == 0) {
                delete cur_fs;
                cout << "Exiting...\n";
                exit(0);
            } else if (tokeninput[0].compare("mkfs") == 0) {
                if (tokeninput.size() != 1) {
                    perror("Usage: mkfs\n");
                } else {
                    fs_state = FS_READY;
                    cout << "Creating and formating file system for use.\n";
                    r = cur_fs->Mkfs();

                    //make sure it was set up correctly
                    if (r != 0) {
                        printf("Error Mkfs failed\n");
                        delete cur_fs;
                        return -1;
                    }
                }
            } else if (check_fs(tokeninput[0])) {
                if (tokeninput[0].compare("open") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <filename> <flags>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        temp = cur_fs->Open(tokeninput[1], tokeninput[2]);
                        if(temp != -1)
                            cout << "File opened successfully with fd=" << temp << endl;
                    }
                } else if (tokeninput[0].compare("read") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <fd> <size>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Read((unsigned int) atoi(tokeninput[1].c_str()), (unsigned int) atoi(tokeninput[2].c_str()));
                    }
                } else if (tokeninput[0].compare("write") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <fd> <string>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Write(atoi(tokeninput[1].c_str()), tokeninput[2]);
                    }
                } else if (tokeninput[0].compare("seek") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <fd> <offset>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Seek(atoi(tokeninput[1].c_str()), atoi(tokeninput[2].c_str()));
                    }
                } else if (tokeninput[0].compare("close") == 0) {
                    if (tokeninput.size() != 2) {
                        printf("Usage: %s <fd>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Close(atoi(tokeninput[1].c_str()));
                    }
                } else if (tokeninput[0].compare("mkdir") == 0) {
                    if (tokeninput.size() != 2) {
                        printf("Usage: %s <dirname>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->MkDir(tokeninput[1]);
                    }
                } else if (tokeninput[0].compare("rmdir") == 0) {
                    if (tokeninput.size() != 2) {
                        printf("'Usage : %s <dirname>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->RmDir(tokeninput[1]);
                    }
                } else if (tokeninput[0].compare("cd") == 0) {
                    if (tokeninput.size() != 2) {
                        printf("Usage: %s <dirname>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Cd(tokeninput[1]);
                    }
                } else if (tokeninput[0].compare("link") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <scr> <dest>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Link(tokeninput[1], tokeninput[2]);
                    }
                } else if (tokeninput[0].compare("unlink") == 0) {
                    if (tokeninput.size() != 2) {
                        printf("Usage: %s <filename>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->UnLink(tokeninput[1]);
                    }
                } else if (tokeninput[0].compare("stat") == 0) {
                    if (tokeninput.size() != 2) {
                        printf("Usage: %s <name>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Stat(tokeninput[1]);
                    }
                } else if (tokeninput[0].compare("ls") == 0) {
                    if (tokeninput.size() != 1) {
                        printf("Usage: %s\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Ls();
                    }
                } else if (tokeninput[0].compare("cat") == 0) {
                    if (tokeninput.size() != 2) {
                        printf("Usage: %s <filename>n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Cat(tokeninput[1]);
                        cout << endl;
                    }
                    //cur_fs->Cat();
                } else if (tokeninput[0].compare("cp") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <src> <dest>n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Cp(tokeninput[1], tokeninput[2]);
                    }
                } else if (tokeninput[0].compare("tree") == 0) {
                    if (tokeninput.size() != 1) {
                        printf("Usage: %s\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Tree();
                    }
                } else if (tokeninput[0].compare("import") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <srcname> <destname>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Import(tokeninput[1], tokeninput[2]);
                    }
                } else if (tokeninput[0].compare("export") == 0) {
                    if (tokeninput.size() != 3) {
                        printf("Usage: %s <srcname> <destname>\n", tokeninput[0].c_str());
                    } else {
                        cout << "Executing " << tokeninput[0] << "...\n";
                        cur_fs->Export(tokeninput[1], tokeninput[2]);
                    }
                } else {
                    cout << "Unknown command: " << tokeninput[0] << "\n";
                }
            } else {
                cout << "Unknown command: " << tokeninput[0] << endl;
            }
        } else {
            continue;
        }
    }

    return 0;
}

bool check_fs(string run_command) {
    if (fs_state != FS_READY) {
        cout << "File system is not ready. Please execute \"mkfs\" command.\n";
        cout << "Then run " << run_command << " again.\n";

        return false;
    }
    return true;
}

vector<string> tokenizeString(string input) {
    vector<string> temp;
    string tempstring;
    int quotefound;
    int escapecharfound;

    unsigned int i;

    tempstring = "";
    quotefound = 0;
    escapecharfound = 0;

    for (i = 0; i < input.length(); i++) {
        if (escapecharfound == 1) {
            if (input[i] == 'n') {
                tempstring += '\n';
            } else if (input[i] == '\"') {
                tempstring += '\"';
            } else if (input[i] == '\\') {
                tempstring += '\\';
            }
            escapecharfound = 0;
        } else if (quotefound == 0) {
            if (input[i] == '\"') {
                tempstring = "";
                quotefound = 1;
            } else if (input[i] == '\\') {
                escapecharfound = 1;
                if (i == input.length() - 1) {
                    tempstring += '\\';
                }
            } else if (input[i] != ' ') {
                tempstring += input[i];
            } else {
                temp.push_back(tempstring);
                tempstring = "";
            }
        } else {
            if (input[i] == '\"') {
                temp.push_back(tempstring);
                tempstring = "";
                quotefound = 0;
            } else if (input[i] == '\\') {
                escapecharfound = 1;
                if (i == input.length() - 1) {
                    tempstring += '\\';
                }
            } else {
                tempstring += input[i];
            }
        }
    }

    if (tempstring.length() != 0) {
        temp.push_back(tempstring);
    }


    return temp;
}
